package eu.jk.web.io.upload;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

import javax.servlet.ServletContext;
import javax.servlet.ServletRequest;

import eu.jk.base.security.Encrypt;


/**
 * 文件上传类<br>
 * @author wenjianwzz
 * 
 */
public class FileUploader {
	/**
	 * 输入流
	 */
	InputStream in;
	File dir;
	HashMap<String, Field> parameters;
	List<UploadFile> files;
	String charset;
	int ContentLength;
	int fileMaxSize = 4 * 1024 * 1024;
	int fileMaxCount = 1;
	List<String> suffix;
	List<String> tooBig;
	List<String> denied;

	/**
	 * 构造函数
	 * @param context
	 * @param request
	 */
	public FileUploader(ServletContext context, ServletRequest request) {
		String basePath = context.getRealPath("");
		ContentLength = request.getContentLength();
		// System.out.println(ContentLength);
		try {
			in = request.getInputStream();
			parameters = new HashMap<String, Field>();
			files = new ArrayList<UploadFile>();
			charset = request.getCharacterEncoding();
			dir = new File(basePath + "/WEB-INF/upload");
			if (!dir.exists())
				dir.mkdir();
		} catch (IOException e) {
			e.printStackTrace();
		}
		suffix = new ArrayList<String>();
		tooBig = new ArrayList<String>();
		denied = new ArrayList<String>();
	}

	/**
	 * 构造函数,指定上传目录
	 * @param context
	 * @param request
	 * @param uploadDir
	 */
	public FileUploader(ServletContext context, ServletRequest request ,String uploadDir) {
		ContentLength = request.getContentLength();
		// System.out.println(ContentLength);
		try {
			in = request.getInputStream();
			parameters = new HashMap<String, Field>();
			files = new ArrayList<UploadFile>();
			charset = request.getCharacterEncoding();
			dir = new File(uploadDir);
			if (!dir.exists())
				dir.mkdir();
		} catch (IOException e) {
			e.printStackTrace();
		}
		suffix = new ArrayList<String>();
		tooBig = new ArrayList<String>();
		denied = new ArrayList<String>();
	}
	
	/**
	 * 将文件上传,获取参数
	 * @return this
	 */
	public FileUploader upload() {
		//long start = new Date().getTime();
		byte[] buffer = new byte[ContentLength];
		FileOutputStream fo = null;
		File file = null;
		int lenPer = 0;
		int len = 0;
		String line = null;
		String name = null;
		String value = null;
		String filename = null;
		UploadFile up = null;
		String tempname = null;
		String fileSuffix = null;
		ByteBuffer wb = ByteBuffer.allocate(fileMaxSize);
		try {// read to buffer
			while (lenPer >= 0) {
				lenPer = in.read(buffer, len, 1000);
				len += lenPer;
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		// System.out.println("io done" + (new Date().getTime() - start));
		int i = 0;
		int last = 0;
		String flag = null;
		boolean readData = false;
		boolean readValue = false;
		boolean denyFlag = false;
		boolean bigFlag = false;
		String[] headers;
		while (i < len) {
			last = i;
			while (i < len && buffer[i] != 13) {
				i++;
			}
			try {
				line = new String(buffer, last, i - last, charset);
			} catch (UnsupportedEncodingException e) {
				e.printStackTrace();
			}

			if (flag == null) {
				flag = '\n' + line;
			}
			if (readData || readValue) {
				if (readData) {// file
					try {
						denyFlag = !suffix.contains(fileSuffix);
						fo = new FileOutputStream(file);
						i += 2;
						// System.out.println("file write start"+ (new Date().getTime() - start));
						while (i < len) {
							if (buffer[i] == 13) {
								if (flag.equals(new String(buffer, i + 1, flag
										.length())))
									break;
							}
							if (wb.hasRemaining())
								wb.put(buffer, i, 1);
							else
								bigFlag = true;
							i++;
						}
						if (!bigFlag) {
							fo.write(wb.array(),0,wb.position());
						}
						wb.clear();
						fo.close();
						readData = false;
						// System.out.println("file done"+ (new Date().getTime() - start));
						if (!bigFlag && !denyFlag) {
							parameters.put(name, new Field(name,
									up = new UploadFile(filename, file)));
							files.add(up);
						} else {
							file.delete();
							if (!denyFlag) {
								tooBig.add(filename);
							} else {
								denied.add(filename);
							}
						}
					} catch (FileNotFoundException e) {
						e.printStackTrace();
					} catch (IOException e) {
						e.printStackTrace();
					}
				} else {// value
					i += 2;
					last = i;
					while (i < len) {
						if (buffer[i] == 13) {
							if (flag.equals(new String(buffer, i + 1, flag
									.length())))
								break;
						}
						i++;
					}
					try {
						value = new String(buffer, last, i - last, charset);
					} catch (UnsupportedEncodingException e) {
						e.printStackTrace();
					}
					readValue = false;
					parameters.put(name, new Field(name, value));
				}
			} else {
				if (line.startsWith("\nContent-Disposition:")) {
					headers = line.split(";");
					name = headers[1].split("=")[1].replace("\"", "");
					// System.out.println(name);
					if (headers.length > 2) {
						filename = headers[2].split("=")[1].replace("\"", "");
						readData = true;
						fileSuffix = filename.substring(filename
								.lastIndexOf('.') + 1);

						tempname = Encrypt
								.md5(new Date().getTime() + filename)
								+ "." + fileSuffix;
						file = new File(dir + "/" + tempname);
						try {
							file.createNewFile();
						} catch (IOException e) {
							e.printStackTrace();
						}
						// System.out.println("file create"+ (new Date().getTime() - start));
						// System.out.println(filename);
						while (i < len && buffer[i] != 13) {
							i++;
						}
						i++;
					} else {
						readValue = true;
					}
					while (i < len && buffer[i] != 13) {
						i++;
					}
					i++;
				}
			}
			i++;
		}
		// System.out.println(new Date().getTime() - start);
		return this;
	}

	/**
	 * 得到文件上传目录
	 * @return
	 */
	public File getDir() {
		return dir;
	}

	/**
	 * 获取参数
	 * @param name
	 * @return 参数or null
	 */
	public Field getParameters(String name) {
		return parameters.get(name);
	}

	/**
	 * 获取上传成功的文件
	 * @return List<UploadFile>
	 */
	public List<UploadFile> getFiles() {
		return files;
	}

	/**
	 * 获取字符编码
	 * @return request.getCharacterEncoding()
	 */
	public String getCharset() {
		return charset;
	}

	/**
	 * 设置单个文件上传限制,单位字节
	 * @param fileMaxSize
	 * @return this
	 */
	public FileUploader setFileMaxSize(int fileMaxSize) {
		this.fileMaxSize = fileMaxSize;
		return this;
	}

	/**
	 * 设置单次上传文件个数限制
	 * @param fileMaxCount
	 * @return this
	 */
	public FileUploader setFileMaxCount(int fileMaxCount) {
		this.fileMaxCount = fileMaxCount;
		return this;
	}

	/**
	 * 增加允许后缀,比如rar
	 * @param suffix
	 * @return
	 */
	public FileUploader addPermitSuffix(String suffix) {
		this.suffix.add(suffix);
		return this;
	}

	/**
	 * 设置允许后缀
	 * @param suffix
	 * @return
	 */
	public FileUploader setSuffix(List<String> suffix) {
		this.suffix = suffix;
		return this;
	}

	/**
	 * 获取因文件太大而不能上传的文件列表
	 * @return
	 */
	public List<String> getTooBig() {
		return tooBig;
	}

	/**
	 * 获取因超出文件个数限制而不能上传的列表
	 * @return
	 */
	public List<String> getDenied() {
		return denied;
	}
	
	/**
	 * 设置上传路径,这个最好在调用upload函数之前进行,因为太晚了就没用了
	 * @param path
	 * @return
	 */
	public FileUploader setUploadPath(String path){
		dir = new File(path);
		if (!dir.exists())
			dir.mkdir();
		return this;
	}
}
